package com.kelp.codetoolkit.service.impl

import com.intellij.database.util.common.isNotNullOrEmpty
import com.intellij.ide.fileTemplates.FileTemplateManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.Messages
import com.intellij.psi.PsiJavaFile
import com.kelp.codetoolkit.cache.TemplateCache
import com.kelp.codetoolkit.constant.GlobalConfig
import com.kelp.codetoolkit.dto.GenerateCodeContentDTO
import com.kelp.codetoolkit.entity.ColumnInfo
import com.kelp.codetoolkit.entity.TableInfo
import com.kelp.codetoolkit.enums.*
import com.kelp.codetoolkit.respository.CodeToolkitGlobalSettingRepository
import com.kelp.codetoolkit.service.CreateBusinessLogicFileService
import com.kelp.codetoolkit.service.TableInfoHandleService
import com.kelp.codetoolkit.service.WriteFileService
import com.kelp.codetoolkit.utils.*
import com.kelp.codetoolkit.utils.frame.SpringUtil


/**
 * @author: liuyuebai
 * @date: 2024/8/2 13:57
 * @description:
 */
class CreateBusinessLoginFileServiceImpl:CreateBusinessLogicFileService {

    override fun databaseToBusinessLoginServiceFIle(project: Project,codeParams: GenerateCodeContentDTO) {
        //------------------------------校验------------------------------------------
        //表获取
        val dbTable = CacheDataUtil.getDbTable()
        if (null == dbTable){
            Messages.showErrorDialog(project, "Please select the database table", "Error")
            return
        }
        //表结构获取
        val tableInfo = TableInfoHandleService.getInstance(project).parseToTableInfo(dbTable)
        val tableName = tableInfo.getTableName()
        if(tableName.isNullOrBlank()){
            Messages.showErrorDialog(project, "Table could not be found.", "Error")
            return
        }
        //Dao、Service等文件生成的module
        val moduleName = codeParams.getModuleName()
        if (moduleName.isNullOrBlank()){
            Messages.showErrorDialog(project, "Please select the module name.", "Error")
            return
        }
        //module
        val module = ProjectUtil.getModule(project, moduleName)
        if (null == module){
            Messages.showErrorDialog(project, "The selected module does not exist.", "Error")
            return
        }
        //自动获取SpringApplication所在的包目录
        val moduleApplicationFile = SpringUtil.getModuleApplicationFile(module)
        if (null == moduleApplicationFile){
            Messages.showErrorDialog(project, "The module application file does not exist.", "Error")
            return
        }
        var businessFilePath = moduleApplicationFile.virtualFile.parent.path

        //------------------------------校验------------------------------------------

        //-------------------------文件代码内容Map---------------------------------------
        val className = NameHandleUtil.convertClassName(tableName)
        val parentPackageName = codeParams.getParentPackageName()
        if (parentPackageName.isNotNullOrEmpty){
            businessFilePath += "$parentPackageName/"
        }
        val writeParentPackageName = SpringUtil.getPackageName(moduleApplicationFile, parentPackageName)
        //文件内容map
        val writeContentParams = getWriteTableContentParams(tableInfo)
        getCommentParams(tableInfo.getComment(),project,writeContentParams)
        //文件内容map  类名
        classNameTOContentMap(writeContentParams,className)
        //设置字段导入包
        writeContentParams["importFieldPackageList"] = handleImportPackage(tableInfo)
        //序列化
        writeContentParams["serialVersionUID"]=CommonToolUtil.generateSerialVersionUID()
        //导入lombok
        writeContentParams["importLombokList"]=CommonPackageHandleUtil.handleLombok()
        //导入公共
        writeContentParams["importCommonList"] = CommonPackageHandleUtil.handleCommonPackage()
        //
        writeContentParams["parentPackageName"]= writeParentPackageName
        val ormTemplate = getOrmTemplate(project)
        //导入MyBatis-plus
        if (OrmFrameworkEnum.MyBatis.value == ormTemplate){
            writeContentParams["importMyBatisPlusList"] = CommonPackageHandleUtil.handleMybatisPlus()
        }
        //-------------------------文件代码内容Map---------------------------------------

        //-------------------------文件Map---------------------------------------
        val writeFileParams = commonWriteFileParam(moduleName,className)

        val javaTemplates = JavaTemplateInfoEnum.get(ormTemplate)
        val serviceTemplate = getMircoServiceTemplate(project)
        val serviceTemplates = JavaTemplateInfoEnum.get(serviceTemplate)
        javaTemplates.addAll(serviceTemplates)
        javaTemplates.forEach { template ->
            val templateCodeContent = TemplateCache.getFileTemplateCodeContent(template.templatePath)
            if(templateCodeContent.isNullOrBlank()){
                return@forEach
            }
            val packageName = getPackageName(template, writeParentPackageName)
            writeContentParams["packageName"] = packageName
            val fileCodeContent = FileContentTemplateUtil.generateFileCodeContent(templateCodeContent, writeContentParams)
            if(JavaTemplateInfoEnum.PROVIDER.templatePath == template.templatePath){
                val globalSettingState =  CodeToolkitGlobalSettingRepository.getInstance(project).state
                val stubModuleName = globalSettingState.getStubModuleName()
                if (stubModuleName.isBlank()){
                    return@forEach
                }
                val providerFilePath = businessFilePath.replace(module.name,stubModuleName)
                    .replace("/"+module.name.replace("-",""),"/"+globalSettingState.getStubPackage())
                writeFileParam(template,className,providerFilePath,fileCodeContent,writeFileParams)
            }else{
                writeFileParam(template,className,businessFilePath,fileCodeContent,writeFileParams)
            }
            WriteFileService.getInstance(project).writeBusinessLogicFile(project, writeFileParams)
        }

        var resourcesPath = SpringUtil.getResourcesPath(module)?:return
        val xmlTemplates = XmlTemplateInfoEnum.get(ormTemplate)
        val writeXmlFileParams = commonWriteFileParam(moduleName,className)
        val writeXmlContentParams = mutableMapOf<String, Any>()
        xmlTemplates.forEach{template->
            run {
                val codeContent = TemplateCache.getFileTemplateCodeContent(template.templatePath)
                if (codeContent.isNullOrBlank()) {
                    return@forEach
                }
                databaseWriteXmlJavaMapper(writeParentPackageName,className,writeXmlContentParams)
                val fileCodeContent = FileContentTemplateUtil.generateFileCodeContent(codeContent, writeXmlContentParams)
                resourcesPath += "/"+template.packageName
                if (parentPackageName.isNotNullOrEmpty){
                    resourcesPath += "/$parentPackageName"
                }
                writeXmlFileParam(template,className,resourcesPath,fileCodeContent,writeXmlFileParams)
                WriteFileService.getInstance(project).writeBusinessLogicFile(project, writeXmlFileParams)
            }
        }
    }


    override fun entityToBusinessLoginServiceFIle(project: Project,psiJavaFile: PsiJavaFile) {
        //------------------------------校验------------------------------------------
        val module = ProjectUtil.getModule(psiJavaFile)
        if (null == module){
            Messages.showErrorDialog(project, "The module could not be found.", "Error")
            return
        }
        val fileParent = psiJavaFile.parent
        if (null == fileParent){
            Messages.showErrorDialog(project, "The parent folder of the selected Java file does not exist.", "Error")
            return
        }
        val className = psiJavaFile.classes[0].name
        if (className.isNullOrBlank()){
            Messages.showErrorDialog(project, "The className could not be found.", "Error")
            return
        }

        val currentPackageName = psiJavaFile.packageName

        if (!currentPackageName.contains("."+MVCLayeringEnum.ENTITY.packageName)){
            Messages.showErrorDialog(project, "Only business implementation files can be created for entity classes under the Entity package.", "Error")
            return
        }

        //------------------------------校验------------------------------------------
        val fileParentPath = fileParent.virtualFile.path
        //文件内容map
        val writeContentParams = mutableMapOf<String, Any>()
        //文件内容map  类名
        classNameTOContentMap(writeContentParams,className)
        getCommentParams("",project,writeContentParams)

        val writeFileParams = commonWriteFileParam(module.name,className)

        var parentPackageName = currentPackageName.replace(MVCLayeringEnum.ENTITY.packageName, "")
        parentPackageName = parentPackageName.substring(0,parentPackageName.length-1)

        val ormTemplate = getOrmTemplate(project)
        val javaTemplates = JavaTemplateInfoEnum.get(ormTemplate)
        val serviceTemplate = getMircoServiceTemplate(project)
        val serviceTemplates = JavaTemplateInfoEnum.get(serviceTemplate)
        javaTemplates.addAll(serviceTemplates)
        javaTemplates.forEach { template ->
            if (MVCLayeringEnum.isEntity(template.packageName)){
                return@forEach
            }
            val templateCodeContent = TemplateCache.getFileTemplateCodeContent(template.templatePath)
            if(templateCodeContent.isNullOrBlank()){
                return@forEach
            }
            val templatePackageName = template.packageName.replace("/", ".")
            val packageName = currentPackageName.replace(MVCLayeringEnum.ENTITY.packageName,templatePackageName)
            writeContentParams["packageName"] = packageName
            writeContentParams["parentPackageName"]= parentPackageName
            if (template.templatePath == JavaTemplateInfoEnum.PROVIDER_IMPL.templatePath){
                val globalSettingState = CodeToolkitGlobalSettingRepository.getInstance(project).state
                writeContentParams["implementInterface"] = packageName.replace(module.name.replace("-",""),globalSettingState.getStubPackage())+"."+className+"Provider"
            }
            if (template.templatePath == JavaTemplateInfoEnum.PROVIDER.templatePath){
                val packageNameArr = packageName.split(".")
                val globalSettingState = CodeToolkitGlobalSettingRepository.getInstance(project).state
                writeContentParams["packageName"] = packageName.replace(packageNameArr[packageNameArr.size-2],globalSettingState.getStubPackage())
            }
            val fileCodeContent = FileContentTemplateUtil.generateFileCodeContent(templateCodeContent, writeContentParams)
            val entityName =  MVCLayeringEnum.ENTITY.packageName
            var filePath = fileParentPath.replace("/$entityName","/"+template.packageName)
            if(template.templatePath == JavaTemplateInfoEnum.PROVIDER.templatePath){
                val globalSettingState =  CodeToolkitGlobalSettingRepository.getInstance(project).state
                val stubModuleName = globalSettingState.getStubModuleName()
                if (stubModuleName.isBlank()){
                    return@forEach
                }
                filePath = filePath.replace(module.name,stubModuleName)
                    .replace("/"+module.name.replace("-",""),"/"+globalSettingState.getStubPackage())
            }
            entityTOWriteFileParam(className,filePath,fileCodeContent,template.templateSuffix,writeFileParams)
            WriteFileService.getInstance(project).writeBusinessLogicFile(project, writeFileParams)
        }
        val resourcesPath = SpringUtil.getResourcesPath(module)?:return
        val xmlTemplates = XmlTemplateInfoEnum.get(ormTemplate)
        val writeXmlContentParams = mutableMapOf<String, Any>()
        val writeXmlFileParams = writeFileParam(module.name,className)
        val applicationPath = SpringUtil.getSpringBootApplicationPath(module)
        xmlTemplates.forEach{template->
            run {
                val templateCodeContent = TemplateCache.getFileTemplateCodeContent(template.templatePath)
                if (templateCodeContent.isNullOrBlank()) {
                    return@forEach
                }
                val packageName = currentPackageName.replace(MVCLayeringEnum.ENTITY.packageName,JavaTemplateInfoEnum.MYBATIS_PLUS_DAO.packageName)
                entityWriteXmlJavaMapper(packageName,className,writeXmlContentParams)
                val fileCodeContent = FileContentTemplateUtil.generateFileCodeContent(templateCodeContent, writeXmlContentParams)
                var xmlMapperPath = fileParentPath.replace(applicationPath,resourcesPath).replace(MVCLayeringEnum.ENTITY.packageName,"")
                xmlMapperPath = xmlMapperPath.substring(0,resourcesPath.length)+"/"+template.packageName+xmlMapperPath.substring(resourcesPath.length,xmlMapperPath.length-1)
                entityTOWriteFileParam(className,xmlMapperPath,fileCodeContent,template.templateSuffix,writeXmlFileParams)
                WriteFileService.getInstance(project).writeBusinessLogicFile(project, writeXmlFileParams)
            }
        }
    }


    private fun getOrmTemplate(project: Project):String{
        var template = OrmFrameworkEnum.MyBatisPlus.value
        val globalSettingState = CodeToolkitGlobalSettingRepository.getInstance(project).state
        if (globalSettingState.getOrmSetting().isNotNullOrEmpty) {
            template = globalSettingState.getOrmSetting()
        }
        return template
    }

    private fun getMircoServiceTemplate(project: Project):String{
        var template = ""
        val globalSettingState = CodeToolkitGlobalSettingRepository.getInstance(project).state
        if (globalSettingState.getRemoteInvocationFramework().isNotNullOrEmpty) {
            val remoteInvocation = RemoteInvocationFrameworkEnum.getRemoteInvocation(globalSettingState.getRemoteInvocationFramework())?:return template
            template = remoteInvocation.templateKey
        }
        return template
    }

    private fun getCommentParams(comment:String?,project: Project, params:MutableMap<String, Any>) {
        val defaultProperties = FileTemplateManager.getInstance(project).defaultProperties
        var author = defaultProperties["USER"]
        if (author == null) {
            author = GlobalConfig.defaultAuthor()
        }
        params["author"] = author
        params["dateTime"] = DateTimeUtil.currentDateTime()
        if (comment?.isNotEmpty() == true) {
            params["comment"] = comment
        }
    }


    private fun classNameTOContentMap(writeContentParams:MutableMap<String, Any>,className:String){
        writeContentParams["className"] = className
        writeContentParams["initialClassName"] = NameHandleUtil.initialLowerCase(className)
    }

    private fun handleImportPackage(tableInfo:TableInfo):MutableSet<String>{
        val importPackageList = mutableSetOf<String>()
        tableInfo.getFullColumns()?.forEach { columnInfo->
            run {
                importPackageList.add(columnInfo.getColumnType())
            }
        }
        return importPackageList
    }

    private fun getPackageName(template: JavaTemplateInfoEnum,writeParentPackageName:String):String{
        val templatePackageName = template.packageName.replace("/", ".")
        return "$writeParentPackageName.$templatePackageName"
    }

    private fun writeFileParam(moduleName:String,className:String): MutableMap<String, String> {
        val params = commonWriteFileParam(moduleName,className)
        return params
    }

    private fun commonWriteFileParam(moduleName:String,className:String): MutableMap<String, String> {
        val params = mutableMapOf<String, String>()
        params["className"] = className
        params["moduleName"] = moduleName
        return params
    }

    private fun writeFileParam(template: JavaTemplateInfoEnum,className:String,businessFilePath:String,fileCodeContent:String,writeFileParams:MutableMap<String, String>){
        writeFileParams["codeContent"] = fileCodeContent
        val currentFileParentPath = businessFilePath+"/"+template.packageName
        writeFileParams["businessFilePath"] = currentFileParentPath
        writeFileParams["fileName"]= className+template.templateSuffix
    }

    private fun entityTOWriteFileParam(className:String,filePath: String,fileCodeContent:String,templateSuffix:String,writeFileParams:MutableMap<String, String>){
        writeFileParams["businessFilePath"] = filePath
        writeFileParams["codeContent"] = fileCodeContent
        writeFileParams["fileName"]= className+templateSuffix
    }

    private fun writeXmlFileParam(template: XmlTemplateInfoEnum,className:String,businessFilePath:String,fileCodeContent:String,writeFileParams:MutableMap<String, String>){
        writeFileParams["codeContent"] = fileCodeContent
        writeFileParams["businessFilePath"] = businessFilePath
        writeFileParams["fileName"]= className+template.templateSuffix
    }

    private fun databaseWriteXmlJavaMapper(writeParentPackageName:String,className: String,writeXmlContentParams: MutableMap<String, Any>){
        val javaMapperPackage = StringBuilder()
        javaMapperPackage.append(writeParentPackageName).append(".").append(MVCLayeringEnum.DAO.packageName).append(".")
        javaMapperPackage.append(className).append("Mapper")
        writeXmlContentParams["javaMapper"] = javaMapperPackage.toString()
    }

    private fun entityWriteXmlJavaMapper(writeParentPackageName:String,className: String,writeXmlContentParams: MutableMap<String, Any>){
        val javaMapperPackage = StringBuilder()
        javaMapperPackage.append(writeParentPackageName).append(".")
        javaMapperPackage.append(className).append("Mapper")
        writeXmlContentParams["javaMapper"] = javaMapperPackage.toString()
    }

    private fun getWriteTableContentParams(tableInfo: TableInfo): MutableMap<String, Any> {
        val params = mutableMapOf<String, Any>()
        params["tableName"] = tableInfo.getTableName() as String
        params["classComment"] = tableInfo.getComment() as String
        params["pkColumn"] = tableInfo.getPkColumn() as ColumnInfo
        params["fullColumns"] = tableInfo.getFullColumns() as MutableList<ColumnInfo>
        return params
    }


}